// let a = new Promise((resolve,reject)=>{

// })
// console.log(a);

class myPromise {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func) {
        this.PromiseState = myPromise.PENDING
        this.PromiseResult = null
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        try {
            func(this.resolve.bind(this), this.reject.bind(this))
        } catch (e) {
            this.reject(e)
        }

    }
    resolve(result) {
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.FULFILLED
                this.PromiseResult = result
                this.onFulfilledCallbacks.forEach((callback) => {
                    callback(result)
                })
            })
        }

    }
    reject(reason) {
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.REJECTED
                this.PromiseResult = reason
                this.onRejectedCallbacks.forEach((callback) => {
                    callback(reason)
                })
            })
        }
    }
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value;
        onRejected = typeof onRejected === 'function' ? onRejected : (reason) => {throw reason};
        // if (this.PromiseState === myPromise.FULFILLED) {
        //     setTimeout(() => {
        //         onFulfilled(this.PromiseResult)
        //     })
        // }
        // if (this.PromiseState === myPromise.REJECTED) {
        //     setTimeout(() => {
        //         onRejected(this.PromiseResult)
        //     });
        // }
        // if (this.PromiseState === myPromise.PENDING) {
        //     this.onFulfilledCallbacks.push(onFulfilled)
        //     this.onRejectedCallbacks.push(onRejected)
        // }

        const promise2 = new myPromise((resolve, reject) => {
            if (this.PromiseState === myPromise.FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.PromiseResult)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch {
                        reject(x)
                    }

                })
            }
            if (this.PromiseState === myPromise.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.PromiseResult)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch {
                        reject(x)
                    }

                });
            }
            if (this.PromiseState === myPromise.PENDING) {
                this.onFulfilledCallbacks.push((result) => {
                    try {
                        let x = onFulfilled(this.PromiseResult)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch {
                        reject(x)
                    }

                })
                this.onRejectedCallbacks.push(reason=>{
                    try {
                        let x = onRejected(this.PromiseResult)
                        resolvePromise(promise2, x, resolve, reject)
                    } catch {
                        reject(x)
                    }
                })
            }
        })
        return promise2
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'))
    }
    if (x instanceof myPromise) {
        if (x.PromiseState === myPromise.PENDING) {
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject)
            }, reject)
        }
        if (x.PromiseState === myPromise.FULFILLED) {
            resolve(x.PromiseResult)
        }
        if (x.PromiseState === myPromise.REJECTED) {
            reject(x.PromiseResult)
        }
    }
    if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
        try {
            var then = x.then
        } catch (e) {
            return reject(e)
        }

        if (typeof then === 'function') {
            let called = false
            try {
                then.call(x,
                    (y) => {
                        if (called) return
                        called = true
                        resolvePromise(promise2, y, resolve, reject)
                    },
                    (r) => {
                        if (called) return
                        called = true
                        reject(r)
                    })
            } catch (e) {
                if (called) return
                called = true
                reject(e)
            }
        } else {
            resolve(x)
        }

    } else {
       return resolve(x)
    }

}

myPromise.deferred = function () {
    let result = {};
    result.promise = new myPromise((resolve, reject) => {
        result.resolve = resolve;
        result.reject = reject;
    });
    return result;
}


module.exports = myPromise